﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace UtilZ.Dotnet.AudioPlayer.Core
{
    /// <summary>
    /// 环形缓存
    /// </summary>
    public class RingBuffer
    {
        private readonly byte[] _buffer;
        /// <summary>
        /// 获取容量
        /// </summary>
        public int Capcity
        {
            get { return this._buffer.Length; }
        }


        /// <summary>
        /// 读数据位置
        /// </summary>
        private int _rpos = 0;

        /// <summary>
        /// 写数据位置
        /// </summary>
        private int _wpos = 0;

        /// <summary>
        /// 写重新开始
        /// </summary>
        private bool? _reWrite = null;



        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="capcity">容量</param>
        public RingBuffer(int capcity)
        {
            if (capcity < 1)
            {
                throw new ArgumentException("缓存容量过小", nameof(capcity));
            }

            this._buffer = new byte[capcity];
        }



        /// <summary>
        /// 写数据,不支持多线程
        /// </summary>
        /// <param name="data">数据</param>
        public void Write(byte[] data)
        {
            this.Write(data, 0, data.Length);
        }

        /// <summary>
        /// 写数据,不支持多线程
        /// </summary>
        /// <param name="data">数据</param>
        /// <param name="index">起始位置索引</param>
        /// <param name="length">数据长度</param>
        public void Write(byte[] data, int index, int length)
        {
            if (data == null)
            {
                throw new ArgumentNullException(nameof(data));
            }

            if (index < 0 || index + length > data.Length)
            {
                throw new ArgumentOutOfRangeException($"起始位置索引和数据长度参数超出范围");
            }

            if (length > this._buffer.Length)
            {
                throw new ArgumentOutOfRangeException($"追加的数据长度超出容量范围");
            }

            try
            {
                int bufferModLength = this._buffer.Length - this._wpos;
                if (bufferModLength >= length)
                {
                    Array.Copy(data, index, this._buffer, this._wpos, length);
                    this._wpos += length;
                }
                else
                {
                    Array.Copy(data, index, this._buffer, this._wpos, bufferModLength);
                    length = length - bufferModLength;
                    index = index + bufferModLength;
                    Array.Copy(data, index, this._buffer, 0, length);
                    this._wpos = length;
                    this._reWrite = true;
                }
            }
            catch (Exception ex)
            {
                throw new Exception($"this._buffer.Length:{this._buffer.Length},index:{index},length:{length},this._wpos:{this._wpos}", ex);
            }
        }


        /// <summary>
        /// 读取指定长度的数据,返回读取到数据长度
        /// </summary>
        /// <param name="destinationArray">存放数据的数组</param>
        /// <param name="destinationIndex">存放数据的数组中起始位置</param>
        /// <param name="length">读取长度</param>
        /// <returns>读取到数据长度</returns>
        public int Read(byte[] destinationArray, int destinationIndex, int length)
        {
            if (this._reWrite.HasValue && this._reWrite.Value)
            {
                int bufferModLength = this._buffer.Length - this._rpos;
                if (bufferModLength >= length)
                {
                    //上一圈上剩下的足够
                    Array.Copy(this._buffer, this._rpos, destinationArray, destinationIndex, length);
                    this._rpos += length;
                    return length;
                }

                //上一圈上剩下的全部读取
                Array.Copy(this._buffer, this._rpos, destinationArray, destinationIndex, bufferModLength);
                destinationIndex += bufferModLength;
                length = length - bufferModLength;
                this._reWrite = false;
                this._rpos = 0;
            }

            //同一圈
            int availableLength = this._wpos - this._rpos;
            if (availableLength >= length)
            {
                //当前读圈上剩下的足够
                Array.Copy(this._buffer, this._rpos, destinationArray, destinationIndex, length);
                this._rpos += length;
                return length;
            }

            //当前读圈上剩下的足够
            Array.Copy(this._buffer, this._rpos, destinationArray, destinationIndex, availableLength);
            this._rpos += availableLength;
            return availableLength;
        }




        /// <summary>
        /// 获取缓存中等处理的数据
        /// </summary>
        /// <returns></returns>
        public byte[] GetWaitData()
        {
            int wpos = this._wpos % this._buffer.Length;
            int rpos = this._rpos % this._buffer.Length;

            byte[] data;

            if (this._reWrite.HasValue && this._reWrite.Value)
            {
                int modLen = this._buffer.Length - rpos;
                data = new byte[modLen + wpos];
                Array.Copy(this._buffer, rpos, data, 0, modLen);
                Array.Copy(this._buffer, 0, data, modLen, wpos);
            }
            else
            {
                if (wpos >= rpos)
                {
                    data = new byte[wpos - rpos];
                    Array.Copy(this._buffer, rpos, data, 0, data.Length);
                }
                else
                {
                    data = new byte[0];
                }
            }

            return data;
        }

        /// <summary>
        /// 获取已播放和未播放的全部数据
        /// </summary>
        /// <returns></returns>
        public byte[] GetAllData()
        {
            int wpos = this._wpos % this._buffer.Length;
            int rpos = this._rpos % this._buffer.Length;

            byte[] data;

            if (this._reWrite.HasValue && this._reWrite.Value)
            {
                data = new byte[this._buffer.Length];
                int modLen = data.Length - wpos;
                Array.Copy(this._buffer, wpos, data, 0, modLen);
                Array.Copy(this._buffer, 0, data, modLen, wpos);
            }
            else
            {
                if (this._reWrite.HasValue)
                {
                    data = new byte[this._buffer.Length];
                    int modLen = data.Length - wpos;
                    Array.Copy(this._buffer, wpos, data, 0, modLen);
                    Array.Copy(this._buffer, 0, data, modLen, wpos);
                }
                else
                {
                    if (wpos >= rpos)
                    {
                        data = new byte[wpos];
                        Array.Copy(this._buffer, 0, data, 0, data.Length);
                    }
                    else
                    {
                        data = new byte[0];
                    }
                }
            }

            return data;
        }

        /// <summary>
        /// 获取已播放和未播放的全部数据长度
        /// </summary>
        /// <returns></returns>
        public int GetAllDataLength()
        {
            int wpos = this._wpos % this._buffer.Length;
            int rpos = this._rpos % this._buffer.Length;
            int length;

            if (this._reWrite.HasValue && this._reWrite.Value)
            {
                length = this._buffer.Length;
            }
            else
            {
                if (this._reWrite.HasValue)
                {
                    length = this._buffer.Length;
                }
                else
                {
                    if (wpos >= rpos)
                    {
                        length = wpos;
                    }
                    else
                    {
                        length = 0;
                    }
                }
            }

            return length;
        }


        /// <summary>
        /// 设置处理位置
        /// </summary>
        /// <param name="pos">指定位置</param>
        public void SetPos(int pos)
        {
            this._rpos = pos;
        }

        /// <summary>
        /// 清空缓存数据
        /// </summary>
        public void Clear()
        {
            this._wpos = 0;
            this._rpos = 0;
            this._reWrite = null;
        }
    }
}
